﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Brain
{
    public class FreeCamera3D : IFreeCamera3D, IBaseComponent, IDrawable
    {
        public bool ShouldSerialize { get; set; }
        public Matrix View { get; set; }
        public Matrix Projection { get; set; }

        public BoundingFrustum Frustum { get; set; }

        public bool ShouldDraw { get; set; }

        /// <summary>
        /// A Matrix representing the Rotation
        /// of this Free Camera
        /// </summary>
        public Matrix RotationMatrix { get; set; }

        public float AspectRatio = 1;
        public float NearPlane { get; set; }
        public float FarPlane { get; set; }
        public float FieldOfView = MathHelper.ToRadians(45);

        public Vector3 Position { get; set; }
        public Vector3 Target { get; set; }

        public Vector3 RotationRadians;
        public Vector3 DegreesRotation
        {
            get { return Vector3Util.ToDegrees(RotationRadians); }
            set { this.RotationRadians = Vector3Util.ToRadians(value); }
        }

        public string Name { get; set; }

        public bool GetAspectRatioFromEngineViewport { get; set; }

        public FreeCamera3D()
        {
            GetAspectRatioFromEngineViewport = true;
            Name = NameUtil.GetUniqueName("Camera3D");

            NearPlane = 1;
            FarPlane = 1000;

            ShouldDraw = true;
            Frustum = new BoundingFrustum(View * Projection);
        }

        public FreeCamera3D(GameScreen gs)
        {
            Name = NameUtil.GetUniqueName("Camera3D");

            NearPlane = 1;
            FarPlane = 1000;

            ShouldDraw = true;
            gs.AddComponent(this);
            Frustum = new BoundingFrustum(View * Projection);
        }

        public Vector3 Up { get; set; }

        public virtual void Draw()
        {
            float aspect = this.AspectRatio;
            if (GetAspectRatioFromEngineViewport)
            {
                aspect = Engine.Instance.GraphicsDevice.Viewport.AspectRatio;
            }

            Projection = CameraUtil.GetProjectionMatrix(this.FieldOfView,
                aspect, this.NearPlane, this.FarPlane);

            this.RotationMatrix = Matrix.CreateRotationX(this.RotationRadians.Y) *
                   Matrix.CreateRotationY(this.RotationRadians.X);

            Vector3 cameraTarget = Vector3.Forward;
            cameraTarget = Vector3.Transform(cameraTarget, RotationMatrix);
            cameraTarget = Position + cameraTarget;

            Vector3 cameraUp = Vector3.Up;
            Up = Vector3.Transform(cameraUp, RotationMatrix);

            this.View = Matrix.CreateLookAt(Position, cameraTarget, Up);

            this.Target = cameraTarget;

            Frustum.Matrix = View * Projection;
        }

        public void SerializeData(SerializationData parentData, string index)
        {
            parentData.AddData("FCam.AspectRatio", this.AspectRatio, index);
            parentData.AddData("FCam.FarPlane", this.FarPlane, index);
            parentData.AddData("FCam.FOV", this.FieldOfView, index);
            parentData.AddData("FCam.Pos", this.Position, index);
            parentData.AddData("FCam.NearPlane", this.NearPlane, index);
            parentData.AddData("FCam.Rot", this.RotationRadians, index);
            parentData.AddData("FCam.Name", this.Name, index);
        }

        public void DeserializeData(SerializationData parentData, string index)
        {
            this.AspectRatio = parentData.GetData<float>("FCam.AspectRatio", index);
            this.FarPlane = parentData.GetData<float>("FCam.FarPlane", index);
            this.FieldOfView = parentData.GetData<float>("FCam.FOV", index);
            this.Position = parentData.GetData<Vector3>("FCam.Pos", index);
            this.NearPlane = parentData.GetData<float>("FCam.NearPlane", index);
            this.RotationRadians = parentData.GetData<Vector3>("FCam.Rot", index);
            this.Name = parentData.GetData<string>("FCam.Name", index);
        }


        public void DeserializedAll()
        {
        }
    }
}
